package cn.nextProject.service;

import cn.nextProject.entity.FormConfig;
import cn.nextProject.entity.flowable.FormDto;
import cn.nextProject.entity.flowable.ProcessDefinitionDTO;
import cn.nextProject.entity.flowable.TaskDTO;
import cn.nextProject.mapper.FormConfigMapper;
import cn.nextProject.service.flowable.ProcessDefinitionDTOService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.*;
import org.flowable.engine.form.FormProperty;
import org.flowable.engine.form.StartFormData;
import org.flowable.engine.impl.form.FormPropertyImpl;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.form.api.FormInfo;
import org.flowable.form.model.SimpleFormModel;
import org.flowable.task.api.Task;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程审批服务
 */
@Slf4j
@Service
public class ProcessService {

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private FormConfigMapper formConfigMapper;

    @Autowired
    private ProcessDefinitionDTOService processDefinitionDTOService;

    /**
     * 根据流程定义id查询流程定义
     * @param processDefinitionId
     * @return
     */
    public ProcessDefinitionDTO findProcessDefinition(String processDefinitionId){
        if (ObjectUtils.isEmpty(processDefinitionId)){
            return null;
        }
        RepositoryService repositoryService = processEngine.getRepositoryService();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();

        ProcessDefinitionDTO dto = ProcessDefinitionDTOService.toDTO(processDefinition);

        return dto;
    }

    public FormConfig getProcessForm(String processDefinitionId){
        if (ObjectUtils.isEmpty(processDefinitionId)){
            return null;
        }
        try {
            RepositoryService repositoryService = processEngine.getRepositoryService();
            FormService formService = (FormService) processEngine.getFormService();

            // 1. 获取流程定义
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(processDefinitionId)
                    .singleResult();
            if (processDefinition == null) {
                throw new RuntimeException("流程定义不存在");
            }

            // 2. 获取 BPMN 模型中的起始事件
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
            Process process = bpmnModel.getProcessById(processDefinition.getKey());
            StartEvent startEvent = (StartEvent) process.getInitialFlowElement();

            // 3. 获取绑定的 formKey
            String formKey = startEvent.getFormKey();

            if (ObjectUtils.isEmpty(formKey)){
                return null;
            }
            Long formId = Long.parseLong(formKey);
            // 4. 通过 formKey 查询表单模型
            FormConfig formConfig = formConfigMapper.findOne(formId);
            return formConfig;
        } catch (Exception e){
            throw new RuntimeException("获取表单失败: " + e.getMessage());
        }
    }

    /**
     * 获取流程定义的BPMN文件内容
     * @param processDefinitionId
     * @return
     */
    public String getProcessBpmn(String processDefinitionId) {
        if (ObjectUtils.isEmpty(processDefinitionId)) {
            throw new IllegalArgumentException("流程定义ID不能为空");
        }

        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 1. 获取流程定义元数据
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();
        if (processDefinition == null) {
            throw new RuntimeException("流程定义不存在");
        }

        // 2. 获取BPMN文件的资源名称（如leave.bpmn20.xml）
        String resourceName = processDefinition.getResourceName();

        // 3. 从数据库读取BPMN文件内容
        try (InputStream bpmnStream = repositoryService.getResourceAsStream(
                processDefinition.getDeploymentId(), resourceName)) {
            if (bpmnStream == null) {
                throw new RuntimeException("未找到BPMN资源文件");
            }
            return IOUtils.toString(bpmnStream, StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 流程部署
     */
    public void deployFlow(String processName){
        Deployment deploy = processEngine.getRepositoryService().createDeployment()
//                .addClasspathResource("processes/" + processName + ".bpmn20.xml") // 部署一个流程
                .addClasspathResource("processes/" + processName) // 部署一个流程
                .name(processName)
                .deploy();
        log.info(deploy.getId());
    }

    /**
     * 流程部署
     */
    public void deployFlowReal(@NotNull MultipartFile file){
        try (InputStream inputStream = file.getInputStream()) {
            Deployment deploy = processEngine.getRepositoryService().createDeployment()
                    .addInputStream(file.getOriginalFilename(), inputStream)  // 添加文件流[3,5](@ref)
                    .name("动态部署_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())) // 设置可识别的部署名称[5](@ref)
                    .key(file.getName()) // 可选：设置部署key[5](@ref)
                    .deploy();

            log.info("部署成功 ID:{} | 名称:{} | 时间:{}",
                    deploy.getId(),
                    deploy.getName(),
                    deploy.getDeploymentTime());
        } catch (IOException e) {
            log.error("流程部署失败: {}", e.getMessage());
            throw new RuntimeException("流程文件读取失败", e);
        }
    }

    /**
     * 发起流程
     * @param processKey
     * @return
     */
    public int startProcess(String processKey,Map<String, Object> variables){
//        //  发起流程需要通过RuntimeService来实现
//        RuntimeService runtimeService = processEngine.getRuntimeService();

        //	方法一：根据 act_re_procdef 表中的id
//        String processId = "Exanple01:2:a7de575b-a73b-11ef-a2cb-005056c00001";
//        //  根据流程定义Id启动 返回的是当前启动的流程实例 ProcessInstance
//        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processId);
//        System.out.println("processInstance.getId() = " + processInstance.getId());
//       	方法二：根据流程图中定义的Key启动
        try {
            //  发起流程需要通过RuntimeService来实现
            RuntimeService runtimeService = processEngine.getRuntimeService();
            runtimeService.startProcessInstanceByKey(processKey,variables);
        }catch (Exception e){
            log.error("启动流程失败",e);
            return 0;
        }
        return 1;
    }

    /**
     * 查询全部流程
     */
    public List<ProcessDefinitionDTO> findAllProcessDefinition (String name){
        RepositoryService repositoryService = processEngine.getRepositoryService();
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
                .processDefinitionNameLike(name + "%")
                .list();
        if (CollectionUtils.isEmpty(list)){
            return new ArrayList<>();
        }

        @NotNull List<ProcessDefinitionDTO> collect = list.stream().map(data -> {

            ProcessDefinitionDTO dto = ProcessDefinitionDTOService.toDTO(data);

            return dto;
        }).collect(Collectors.toList());

        return collect;

    }

    /**
     * 更新流程定义状态
     */
    public void updateSuspended(String processDefinitionId){
        RepositoryService repositoryService = processEngine.getRepositoryService();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();
        // 获取流程定义的状态
        boolean suspended = processDefinition.isSuspended();
        log.info("suspended:{}",suspended);
        if(suspended){
            // 表示被挂起
            log.info("激活流程定义");
            repositoryService.activateProcessDefinitionById(processDefinitionId,true,null);
        }else{
            // 表示激活状态
            log.info("挂起流程");
            repositoryService.suspendProcessDefinitionById(processDefinitionId,true,null);
        }
    }

    @Transactional
    public void deleteProcessDefinition(String processDefinitionId){
        try {
            RepositoryService repositoryService = processEngine.getRepositoryService();
            // 删除流程定义
            repositoryService.deleteDeployment(processDefinitionId,true);
        }catch (Exception e){
            log.error("删除流程定义失败:{}",e);
        }
    }

    /**
     * 为流程定义配置表单
     * @param processDefinitionId 流程定义的ID
     * @param formKey 表单的键
     */
    @Transactional
    public void configureFormForProcessDefinition(String processDefinitionId, Long formKey) throws JsonProcessingException {
        // 参数校验（使用Apache Commons Lang3）
        if (StringUtils.isBlank(processDefinitionId)) {
            throw new IllegalArgumentException("流程定义ID不能为空");
        }
        if (formKey == null || formKey <= 0) {
            throw new IllegalArgumentException("无效的表单配置ID");
        }

        RepositoryService repositoryService = processEngine.getRepositoryService();
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 1. 获取流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();
        if (processDefinition == null) {
            throw new RuntimeException("流程定义不存在" +  processDefinitionId);
        }

        //1.通过 RepositoryService 加载流程定义的 BPMN 模型对象：
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        Process process = bpmnModel.getMainProcess();

        // 2. 获取表单配置
        FormConfig formConfig = formConfigMapper.findOne(formKey);
        if (ObjectUtils.isEmpty(formConfig)){
            throw new RuntimeException("表单配置不存在");
        }

        // 3. 解析JSON配置
//        ObjectMapper objectMapper = new ObjectMapper();
//        Map<String, Object> formFields = objectMapper.readValue(
//                formConfig.getFormJson(),
//                new TypeReference<Map<String, Object>>() {}
//        );

        //4.修改开始事件的 formKey 属性（需确保表单已部署）
        StartEvent startEvent = (StartEvent) process.getInitialFlowElement();
        startEvent.setFormKey(String.valueOf(formKey)); // 表单的唯一标识符（与JSON表单的key一致）

        // 4. 转换 Map<String, Object> 为 Map<String, String>
//        Map<String, String> stringFormFields = new HashMap<>();
//        for (Map.Entry<String, Object> entry : formFields.entrySet()) {
//            if (entry.getValue() != null) {
//                stringFormFields.put(entry.getKey(), entry.getValue().toString());
//            } else {
//                // 可选：处理 null 值的情况，比如跳过或设置空字符串
//                stringFormFields.put(entry.getKey(), "");
//            }
//        }
        //4.删除旧版本
        deleteProcessDefinition(processDefinition.getDeploymentId());

        // 3. ✅ 创建新部署
        Deployment deployment = repositoryService.createDeployment()
                .name("Form-Updated: " + processDefinition.getName())
                .addBpmnModel(processDefinition.getKey() + ".bpmn20.xml", bpmnModel)
                .deploy();

        // 4. 挂起旧版本（可选）
//        repositoryService.suspendProcessDefinitionById(processDefinitionId);

        log.info("流程定义 {} 的表单配置已更新，新部署ID: {}", processDefinitionId, deployment.getId());
    }


    /**
     * 查询流程定义配置表单
     * @param processDefinitionId 流程定义的ID
     */
    @Transactional
    public Map<Integer, Object> findFormForProcessDefinition(String processDefinitionId) throws JsonProcessingException {
        Map<Integer,Object> returnMap = new HashMap<>();
        // 参数校验（使用Apache Commons Lang3）
        if (StringUtils.isBlank(processDefinitionId)) {
            returnMap.put(0,"流程定义ID不能为空");
            return returnMap;
        }

        RepositoryService repositoryService = processEngine.getRepositoryService();
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 1. 获取流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();
        if (processDefinition == null) {
            returnMap.put(0, processDefinitionId + "流程定义不存在");
            return returnMap;
        }

        //1.通过 RepositoryService 加载流程定义的 BPMN 模型对象：
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        Process process = bpmnModel.getMainProcess();

        //4.修改开始事件的 formKey 属性（需确保表单已部署）
        StartEvent startEvent = (StartEvent) process.getInitialFlowElement();
        String formKey = startEvent.getFormKey();// 表单的唯一标识符（与JSON表单的key一致）
        if (ObjectUtils.isEmpty(formKey)){
            returnMap.put(0,"表单不存在");
            return returnMap;
        }
        // 3. 获取表单配置
        FormConfig formConfig = formConfigMapper.findOne(Long.valueOf(formKey));
        if (ObjectUtils.isEmpty(formConfig)){
            returnMap.put(0, "表单配置不存在");
            return returnMap;
        }

        returnMap.put(1, formConfig);
        return returnMap;

    }



}
